from numpy import (
    ndarray,  # ndarray 类型
    array,  # 创建数组
    arange,  # 创建范围数组
    zeros,  # 创建全零数组
    ones,  # 创建全一数组
    arccos,  # 计算反余弦
    pi,  # 圆周率
)

from src.config.config import (
    Config,  # 配置类
)
from src.data.user import (
    User,  # 用户类
)


class UserManager:
    """
    UserManager 类管理所有用户对象。
    """

    def __init__(
            self,
            config: Config,
    ) -> None:
        """
        初始化 UserManager 实例。

        参数:
        - config: 配置对象，包含随机数生成器、日志记录器等配置信息。
        """

        self.rng = config.rng  # 随机数生成器
        self.logger = config.logger.getChild(__name__)  # 日志记录器

        self.users: list = []  # 用户对象列表
        self._initialize_users(config=config)  # 初始化用户

        self.logger.info('user setup complete')  # 记录用户设置完成的日志

    def calc_spherical_coordinates(
            self,
            config,
    ) -> (ndarray, list): # type: ignore
        """
        计算用户的球面坐标。

        参数:
        - config: 配置对象，包含用户数量、用户距离等信息。

        返回:
        - user_spherical_coordinates: 用户的球面坐标（半径、倾斜角、离开角）
        """

        # 计算平均用户位置
        user_pos_average = (arange(0, config.user_nr) - (config.user_nr - 1) / 2) * config.user_dist_average

        # 在用户距离上添加随机值
        random_factor = self.rng.uniform(low=-config.user_dist_bound,
                                         high=config.user_dist_bound,
                                         size=config.user_nr)
        user_dist = user_pos_average + random_factor  # 用户实际距离

        # 计算用户的AOD偏差（弧度）
        user_aods_diff_earth_rad = zeros(config.user_nr)

        for user_idx in range(config.user_nr):
            # 根据用户距离计算AOD偏差
            if user_dist[user_idx] < 0:
                user_aods_diff_earth_rad[user_idx] = -1 * arccos(1 - 0.5 * (user_dist[user_idx] / config.radius_earth)**2)
            else:
                user_aods_diff_earth_rad[user_idx] = arccos(1 - 0.5 * (user_dist[user_idx] / config.radius_earth)**2)

        # 用户中心AOD（弧度）
        user_center_aod_earth_rad = config.user_center_aod_earth_deg * pi / 180

        # TODO: 如果任何用户位置平均值为0，可能需要对偶数和奇数情况进行区分

        # 计算用户的AOD（弧度）
        user_aods_earth_rad = user_center_aod_earth_rad + user_aods_diff_earth_rad

        # 创建用户对象所需的球面坐标
        user_radii = config.radius_earth * ones(config.user_nr)  # 用户半径
        user_inclinations = pi / 2 * ones(config.user_nr)  # 用户倾斜角

        # 用户的球面坐标数组（半径、倾斜角、离开角）
        user_spherical_coordinates = array([user_radii, user_inclinations, user_aods_earth_rad])

        return user_spherical_coordinates

    def _initialize_users(
            self,
            config: Config,
    ) -> None:
        """
        初始化用户对象并添加到用户列表中。

        参数:
        - config: 配置对象，包含用户参数等信息。
        """

        user_spherical_coordinates = self.calc_spherical_coordinates(config=config)  # 计算用户球面坐标

        for user_idx in range(config.user_nr):
            # 创建用户对象并添加到用户列表中
            self.users.append(
                User(
                    idx=user_idx,
                    spherical_coordinates=user_spherical_coordinates[:, user_idx],
                    **config.user_args,
                )
            )

    def update_positions(
            self,
            config,
    ) -> None:
        """
        更新所有用户的位置。

        参数:
        - config: 配置对象，包含用户位置的配置信息。
        """

        user_spherical_coordinates = self.calc_spherical_coordinates(config=config)  # 计算新的用户球面坐标

        for user in self.users:
            # 更新每个用户的位置
            user.update_position(
                spherical_coordinates=user_spherical_coordinates[:, user.idx],
            )
